;; The first three lines of this file were inserted by DrRacket. They record metadata
;; about the language level of this file in a form that our tools can easily process.
#reader(lib "htdp-advanced-reader.ss" "lang")((modname main) (read-case-sensitive #t) (teachpacks ()) (htdp-settings #(#t constructor repeating-decimal #t #t none #f () #f)))
;========
;October 2
;Dormancy
;========
;Require Packages
(require racket)
(require 2htdp/universe)
(require 2htdp/image)
(require test-engine/racket-tests)

(define-struct brick [x y lv] #:transparent)
; brick 是结构体
; (make-brick Number Number Number)
; 解释:brick-x 砖块x坐标
;      brick-y 砖块y坐标
;      brick-lv 砖块等级,等级为 1 或 2 或 3
; 要求结构体透明,以方便单元测试


; Number -> Number
; 随机产生砖块的级别
(define (random-level max)
  (add1 (random max)))

;砖块规模
(define BRICK-LX 50)
(define BRICK-LY 20)
; 砖块50*20像素
(define LV1-COLOR "yellow")
(define LV2-COLOR "blue")
(define LV3-COLOR "red")
(define LV4-COLOR "black")
; 四种级别砖块的颜色[道具方块]
;黄色砖块为:己方板加长
;蓝色砖块为:板移动速度加快
;红色砖块为:在对面区域生成单向通过隔板持续5s
;黑色砖块为:对面板变短
;===========================================
(define LV1-BRICK (rectangle BRICK-LX BRICK-LY "solid" LV1-COLOR))
(define LV2-BRICK (rectangle BRICK-LX BRICK-LY "solid" LV2-COLOR))
(define LV3-BRICK (rectangle BRICK-LX BRICK-LY "solid" LV3-COLOR))
(define LV4-BRICK (rectangle BRICK-LX BRICK-LY "solid" LV4-COLOR))
; width of background
(define WIDTH 600)
; height of background
(define HEIGHT 1000)
; 游戏场地600*1000像素
; Number Number -> brick
; 产生第1级方块,位置为(x,y)
(check-expect (level-1-brick 1 1) (make-brick 1 1 1))
(define (level-1-brick x y)
  (make-brick x y 1))

; Number Number -> brick
; 产生第2级方块,位置为(x,y)
(define (level-2-brick x y)
  (make-brick x y 2))

; Number Number -> brick
; 产生第3级方块,位置为(x,y)
(define (level-3-brick x y)
  (make-brick x y 3))
; Number Number -> brick
; 产生第4级方块,位置为(x,y)
(define (level-4-brick x y)
  (make-brick x y 4))


(define BW (empty-scene WIDTH HEIGHT))
; 游戏场地(空白背景)

(define (brick-img id)
  (cond
    [(= id 1) LV1-BRICK]
    [(= id 2) LV2-BRICK]
    [(= id 3) LV3-BRICK]
    [(= id 4) LV4-BRICK]
    [else (error "Error!")]))

;将所有砖块放置到背景上
(define (bricks-render* bs)
  (cond
    [(empty? bs) BW]
    [else (place-image
           (brick-img (brick-lv (first bs)))
           (brick-x (first bs)) (brick-y (first bs)) 
           (bricks-render* (rest bs)))]))

(define (n-bricks n)
  (if (zero? n)
      '()
      (cons (make-brick (random 0 600) (random 100 600) (random-level 4))
            (n-bricks (sub1 n)))))

(define-struct ball [x y vx vy])
; ball 是结构体
; (make-ball Number Number Number Number)
; 解释:ball-x 砖块的x坐标
;      ball-y y坐标
;      ball-vx x方向速度
;      ball-vy y方向速度

(define-struct board [x vx LENGTH])
; board 是结构体
; (make-ball Number Number)
; 解释: board-x 板的x坐标
;       board-vx 板的x方向速度,为 +1 或者 -1
;       board-BOARD-LENGTH 板的长度

(define-struct worldstate [board-1 ball-1 board-2 ball-2 bricks])
; worldstate 是结构体,描述世界状态
; (make-worldstate board ball [List-of brick])
; 解释: worldstate-board(1,2) 板的状态
;       worldstate-ball(1,2) 球的状态

;定义球和板的尺寸
(define BALL-SIZE 10)
(define BOARD-LENGTH 100)
(define ball-1 (square BALL-SIZE "solid" "green"))
(define ball-2 (square BALL-SIZE "solid" "orange"))
(define (board-img-1 LENGTH)
  (rectangle LENGTH 10 "solid" "green"))
(define (board-img-2 LENGTH)
  (rectangle LENGTH 10 "solid" "orange"))

(define board-1-HEIGHT 900)
(define board-2-HEIGHT 100)
(define (world-render ws)
  (place-image/align
   (board-img-1 (board-LENGTH (worldstate-board-1 ws)))
   (board-x (worldstate-board-1 ws)) board-1-HEIGHT
   "left" "top"
   (place-image/align
    ball-1
    (ball-x (worldstate-ball-1 ws)) (ball-y (worldstate-ball-1 ws))
    "left" "top"
    (place-image/align
    (board-img-2 (board-LENGTH (worldstate-board-2 ws)))
    (board-x (worldstate-board-2 ws)) board-2-HEIGHT
    "left" "top"
    (place-image/align
    ball-2
    (ball-x (worldstate-ball-2 ws)) (ball-y (worldstate-ball-2 ws))
    "left" "top"
    (bricks-render* (worldstate-bricks ws)))))))
;将两个球和两个板放置到画布中
;组件位置与速度（初始）
(define my-ball-1 (make-ball 300 500 5 -5))
(define my-ball-2 (make-ball 300 200 5 -5))
(define my-board-1 (make-board 250 0 100));只有水平速度
(define my-board-2 (make-board 250 0 100))
(define my-bricks (n-bricks 10));砖块数目
(define my-world (make-worldstate my-board-1 my-ball-1 my-board-2 my-ball-2 my-bricks))
;定义my-ball-1位置在(300,500),vx=5,vy=-5
;定义my-ball-2位置在(300,200),vx=5,vy=-5
;定义my-board-1位置在(250,600),vx=0,板长度为100
;定义my-board-2位置在(250,100),vx=0,板长度为100


;清除被击中的方块
(define (kick-brick brs ball-1 ball-2)
  (if (empty? brs)
      '()
      (if (or (hit? (first brs) ball-1) (hit? (first brs) ball-2))
          (kick-brick (rest brs) ball-1 ball-2)
          (cons (first brs)
                (kick-brick (rest brs) ball-1 ball-2)))))


;球与砖块碰撞
(define (hit? br ball)
  (and (>= (ball-x ball) (- (brick-x br) 0));下限
       (>= (ball-y ball) (- (brick-y br) BRICK-LY))
       (<= (ball-x ball) (+ (+ (brick-x br) BRICK-LX) 0));上限
       (<= (ball-y ball) (+ (+ (brick-y br) BRICK-LY) 0))))

(define (hit-one? brs ball)
  (if (empty? brs)
      #false
      (or (hit? (first brs) ball)
          (hit-one? (rest brs) ball))))

;砖块左右边缘
(define (hit-one-x? brs ball)
  (if (empty? brs)
      #false
      (or (and (hit? (first brs) ball)
               (or
                (< (- (ball-x ball) (ball-vx ball)) (brick-x (first brs)))
                (> (- (ball-x ball) (ball-vx ball)) (+ (brick-x (first brs)) BRICK-LX))))
          (hit-one-x? (rest brs) ball))))
;砖块上下边缘
(define (hit-one-y? brs ball)
  (if (empty? brs)
      #false
      (or (and (hit? (first brs) ball)
               (or
                (< (- (ball-y ball) (ball-vy ball)) (brick-y (first brs)))
                (> (- (ball-y ball) (ball-vy ball)) (+ (brick-y (first brs)) BRICK-LY))))
          (hit-one-y? (rest brs) ball))))

;球与板相碰
(define (hit-board-21? ball-2 board-1 LENGTH)
  (and (>= (ball-x ball-2) (board-x board-1))
       (<= (ball-x ball-2) (+ (board-x board-1) LENGTH))
       (>= (ball-y ball-2) (- board-1-HEIGHT 10))
       (<= (ball-y ball-2) (+ board-1-HEIGHT 10))))

(define (hit-board-22? ball-2 board-2 LENGTH)
  (and (>= (ball-x ball-2) (board-x board-2))
       (<= (ball-x ball-2) (+ (board-x board-2) LENGTH))
       (>= (ball-y ball-2) (- board-2-HEIGHT 10))
       (<= (ball-y ball-2) (+ board-2-HEIGHT 10))))

(define (hit-board-11? ball-1 board-1 LENGTH)
  (and (>= (ball-x ball-1) (board-x board-1))
       (<= (ball-x ball-1) (+ (board-x board-1) LENGTH) 5)
       (>= (ball-y ball-1) (- board-1-HEIGHT 10))
       (<= (ball-y ball-1) (+ board-1-HEIGHT 10))))

(define (hit-board-12? ball-1 board-2 LENGTH)
  (and (>= (ball-x ball-1) (board-x board-2))
       (<= (ball-x ball-1) (+ (board-x board-2) LENGTH))
       (>= (ball-y ball-1) (- board-2-HEIGHT 10))
       (<= (ball-y ball-1) (+ board-2-HEIGHT 10))))

;判断球撞击砖块的lv以及颜色
(define (hit-props brs ball lv)
  (if (empty? brs)
      #f
  (if (and (hit? (first brs) ball) (= (brick-lv (first brs)) lv))
      #t
      (hit-props (rest brs) ball lv))))

(define (props-1 brs ball-1 board-1 ball-2 board-2)
  (cond [(hit-props brs ball-1 1)
       (make-board (+ (board-x board-1) (board-vx board-1)) (board-vx board-1) (+ (board-LENGTH board-1) 20))]
        [(hit-props brs ball-2 4)
       (make-board (+ (board-x board-1) (board-vx board-1)) (board-vx board-1) (- (board-LENGTH board-1) 20))]
      [(make-board (+ (board-x board-1) (board-vx board-1)) (board-vx board-1) (board-LENGTH board-1))]))

(define (props-2 brs ball-1 board-1 ball-2 board-2)
  (cond
        [(hit-props brs ball-1 4)
       (make-board (+ (board-x board-2) (board-vx board-2)) (board-vx board-2) (- (board-LENGTH board-2) 20))]
        [(hit-props brs ball-2 1)
         (make-board (+ (board-x board-2) (board-vx board-2)) (board-vx board-2) (+ (board-LENGTH board-2) 20))]
      [(make-board (+ (board-x board-2) (board-vx board-2)) (board-vx board-2) (board-LENGTH board-2))]))

;场地边缘，注意球的直径
(define (hit-vertical-edge? ball)
  (or (< (ball-y ball) 10) (> (ball-y ball) (- HEIGHT 10))))
(define (hit-horizontal-edge? ball)
  (or (< (ball-x ball) 10) (> (ball-x ball) (- WIDTH 10))))


(define (vx-r brs ball); 两种情况可以改变x的速度
  (if (or (hit-one-x? brs ball) (hit-horizontal-edge? ball))
      (* -1 (ball-vx ball))
      (ball-vx ball)))

(define (vy-r brs ball board-1 board-2 LENGTH-1 LENGTH-2); 四种情况可以改变y的速度
  (if (or (hit-one-y? brs ball) (hit-vertical-edge? ball) (hit-board-11? ball board-1 LENGTH-1) (hit-board-21? ball board-1 LENGTH-1) (hit-board-12? ball board-2 LENGTH-2) (hit-board-22? ball board-2 LENGTH-2))
      (* -1 (ball-vy ball))
      (ball-vy ball)))

(define (next-ball brs ball board-1 board-2 LENGTH-1 LENGTH-2); 更新球的速度与坐标
  (make-ball (+ (ball-x ball) (vx-r brs ball))
             (+ (ball-y ball) (vy-r brs ball board-1 board-2 LENGTH-1 LENGTH-2))
             (vx-r brs ball)
             (vy-r brs ball board-1 board-2 LENGTH-1 LENGTH-2)))

(define (tock ws)
  (make-worldstate
   (props-1 (worldstate-bricks ws) (worldstate-ball-1 ws) (worldstate-board-1 ws) (worldstate-ball-2 ws) (worldstate-board-2 ws))
   (next-ball (worldstate-bricks ws) (worldstate-ball-1 ws) (worldstate-board-1 ws) (worldstate-board-2 ws) (board-LENGTH (worldstate-board-1 ws)) (board-LENGTH (worldstate-board-2 ws)))
   (props-2 (worldstate-bricks ws) (worldstate-ball-1 ws) (worldstate-board-1 ws) (worldstate-ball-2 ws) (worldstate-board-2 ws))
   (next-ball (worldstate-bricks ws) (worldstate-ball-2 ws) (worldstate-board-1 ws) (worldstate-board-2 ws) (board-LENGTH (worldstate-board-1 ws)) (board-LENGTH (worldstate-board-2 ws)))
   (kick-brick (worldstate-bricks ws) (worldstate-ball-1 ws)(worldstate-ball-2 ws))))

;按键控制板的运动
(define (keyh ws ke)
  (make-worldstate
   (cond
     [(key=? ke "left") (make-board (board-x (worldstate-board-1 ws))
                                   -8 (board-LENGTH (worldstate-board-1 ws)))]
     [(key=? ke "right") (make-board (board-x (worldstate-board-1 ws))
                                    +8 (board-LENGTH (worldstate-board-1 ws)))]
     [else  (worldstate-board-1 ws)])
   (worldstate-ball-1 ws)
      (cond
  [(or (key=? ke "a") (key=? ke "A")) (make-board (board-x (worldstate-board-2 ws))
                                  -8 (board-LENGTH (worldstate-board-2 ws)))]
    [(or (key=? ke "d") (key=? ke "D")) (make-board (board-x (worldstate-board-2 ws))
                                  8 (board-LENGTH (worldstate-board-2 ws)))]
    [else (worldstate-board-2 ws)])
      (worldstate-ball-2 ws)
   (worldstate-bricks ws)))


(define (release ws ke)
  (make-worldstate
   (cond
     [(key=? ke "left") (make-board (board-x (worldstate-board-1 ws))
                                   0 (board-LENGTH (worldstate-board-1 ws)))]
     [(key=? ke "right") (make-board (board-x (worldstate-board-1 ws))
                                    0 (board-LENGTH (worldstate-board-1 ws)))]
     [else (worldstate-board-1 ws)])
   (worldstate-ball-1 ws)
     (cond
    [(or (key=? ke "a") (key=? ke "A")) (make-board (board-x (worldstate-board-2 ws))
                                  0 (board-LENGTH (worldstate-board-2 ws)))]
    [(or (key=? ke "d") (key=? ke "D")) (make-board (board-x (worldstate-board-2 ws))
                                  0 (board-LENGTH (worldstate-board-2 ws)))]
   [else (worldstate-board-2 ws)])
   (worldstate-ball-2 ws)
   (worldstate-bricks ws)))

;运行程序
(define (main ws)
  (big-bang ws
    [to-draw world-render]
    [on-tick tock]
    [on-key keyh]
    [on-release release]))
(main my-world)